package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.common.UAFCommonRuntimeException;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.UAFRegInitProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.PolicyNotFoundException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uas.plugin.caservice.CertServicePlugin;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.gmrz.util.Strings;
import com.gmrz.util.db.DBUtil;
import com.google.gson.Gson;
import com.google.inject.Inject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class UAPCertRegInitProcessor extends UAFRegInitProcessor {
    private static final Logger LOG = LogManager.getLogger(UAPCertRegInitProcessor.class);
    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }
    @Override
    public List<RegistrationRequest> request(String plainUserName, String hashUserName, String appID, String deviceID,
                                             String authType, String transType, byte[] challengeParam, String dn)
            throws PolicyNotFoundException, DAOException,PluginException {
        RegistrationRequest request = GuiceUtil.getProcessorInjector()
                .getInstance(RegistrationRequest.class);

        Policy p = this.policyManager.getNamedPolicy(authType, appID, transType);
        LOG.info("Policy:" + UAFSchemaBuilder.getGson().toJson(p));
        if (p == null) {
            LOG.error("Requested policy {} is not available on server", "POLICY_" + appID + "_" + authType);
            throw new PolicyNotFoundException(UAFErrorCode.BIZ_POLICY_NOT_FOUND);
        }
        Connection conn = null;
        try {
            conn = UAFDBConnectionMgr.getConnection();
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            int certDeviceMaxCount = 0;
            // 查询允许的最大注册设备数量(允许同一个用户可以进行证书注册的的设备数)
            String maxNumStr = serviceManager.getPluginConfig(Constants.CERTIFICA).get(Constants.DEVICE_MAX_NUM+"."+appID);
            if (Strings.isNotBlank(maxNumStr)) {
                certDeviceMaxCount = Integer.parseInt(maxNumStr);
            }
            // 查询使用证书已注册设备的数量
            int activeRegCount = adao.getCertActiveRegCount(hashUserName,authType);
            if (activeRegCount >= certDeviceMaxCount) {
                throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_REG_MAX_FAILED);
            }
        } catch (SQLException e) {
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR);
        } finally {
            DBUtil.close(conn);
        }
        // 查询已经注册了的匹配条件【已经注册了的aaid[]、keyids[]】
        List<MatchCriteria> mcList = getMCListForAlreadyRegistered(hashUserName, appID, deviceID, authType, transType);
        if (mcList != null) {
            p.appendDisallowed(mcList);
        }
        byte[] challenge = getRandom(32);
        challenge = ArrayUtils.addAll(challenge, challengeParam);
        // 将挑战值、策略名称、未hash的用户名放入“serverdata”中，用于连接服务端两个接口之前的session
        request.getHeader().getServerData().withChallenge(challenge)
                .withPolicyName(p.getPolicyName()).withUserName(plainUserName);
        // 查询可信FacetIDs列表
        request.getHeader().setApplicationID(this.getServerConfig().getApplicationID(appID,transType));

        request.getChallenge().setChallenge(challenge);
        request.setPolicy(p);
        request.setUsername(getString(hashUserName) + "_" + getString(authType) + "_" + getString(transType));
        if (serverConfig.getAppIdUrlBoolean(appID,transType)) {
            setFacetsAaid(request, appID);
        }
        // 证书的dn，经过了base64编码的字符串
        if(Strings.isNotBlank(dn)) {
            CertServicePlugin certServicePlugin = serviceManager.getCertServicePlugin(authType);
            Map<String,String> dnMap = certServicePlugin.getDN(new String(Convert.fromBase64(dn)));
            dn = Convert.toBase64(dnMap.get(Constants.CERT_DN).getBytes());
            Gson gson = UAFSchemaBuilder.getGson();
            // 扩展
            List<Extension> extensions = request.getHeader().getExtensions();
            if (null == extensions) {
                extensions = new ArrayList<Extension>();
                request.getHeader().setExtensions(extensions);
            }
            // 扩展具体实体
            ExtBean extBean = new ExtBean();
            extBean.setDn(dn);
            Extension extension = new Extension();
            extension.setId("cert");
            extension.setData(Convert.toBase64(gson.toJson(extBean).getBytes()));
            extensions.add(extension);
            byte[] dnHash = CryUtil.getSHA256(Convert.fromBase64(dn));
            // 将使用sha-256 摘要算法hash后的dn放入“serverdata”中
            request.getHeader().getServerData().putValidateDataHash(Constants.CERT_DN,Convert.toBase64(dnHash));
            request.getHeader().getServerData().putValidateDataHash(Constants.CERT_DN_JSON,Convert.toBase64(dnMap.get(Constants.CERT_DN_JSON).getBytes()));

        }
        List<RegistrationRequest> requests = new ArrayList<RegistrationRequest>();
        requests.add(request);
        return requests;
    }
    public String getString(String str){
        byte[] strBytes = org.bouncycastle.util.Strings.toUTF8ByteArray(str);
        String string = org.bouncycastle.util.Strings.fromUTF8ByteArray(strBytes);
        return com.alibaba.druid.util.StringUtils.subString( StringUtils.trim(StringUtils.join(string,"")), null, null);
    }
}
